package com.zl.codereview.git;

import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.zl.codereview.git.model.GitAuthor;
import com.zl.codereview.git.model.ProjectFileCode;
import com.zl.codereview.git.model.ProjectQuery;
import com.zl.codereview.git.model.ReviewProjectFile;
import com.zl.codereview.git.model.ReviewProjectResp;
import com.zl.codereview.review.CodeReviewProject;
import com.zl.codereview.review.ReviewContent;
import com.zl.codereview.review.ReviewEntity;
import com.zl.codereview.util.DateUtils;
import com.zl.codereview.util.IdeaUtils;
import com.zl.codereview.util.ThreadServiceUtil;
import com.zl.codereview.util.ObjectUtils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.blame.BlameResult;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.diff.RawText;
import org.eclipse.jgit.diff.RawTextComparator;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.io.DisabledOutputStream;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.zl.codereview.git.JgitUtil.openRpo;

public class GitService {

    public static GitService gitService = new GitService();

    public static GitService getInstance(){
        return gitService;
    }

    public  List<ReviewProjectResp> getProject(ProjectQuery query, Project project) {
        Date startDate = query.getStartDate();
        if(ObjectUtils.isEmpty(startDate)){
            //从一月前开始
            startDate = DateUtils.getLastDay(new Date(),30);
            query.setStartDate(startDate);
        }
        List<ReviewProjectResp> respList = getRespList(project);
        ThreadServiceUtil.execute((projectResp)->{
            List<ReviewProjectFile> files = gitFastFiles(projectResp.getPath(), query);
            for(ReviewProjectFile file : files){
                file.setPath(projectResp.getPath()+"/"+file.getPath());
            }
            projectResp.setFiles(files);

        },respList);
        return  respList;
    }

    /**
     * 查询当前分支名称
     */
    public String branchName(String path){
        Git git = openRpo(path);
        // 获取当前所处的分支名称
        try {
            return git.getRepository().getBranch();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    private List<String> getPaths(String path,String author,Date startDate){
        List<String> paths = new ArrayList<>();
        Git git = openRpo(path);
        try {
            Repository repository = git.getRepository();
            Iterator<RevCommit> iterator = git.log().call().iterator();

            while (iterator.hasNext()) {
                RevCommit commit = iterator.next();
                Date date = new Date(commit.getCommitTime() * 1000L);
                if (date.after(startDate)) {
                    PersonIdent authorIdent = commit.getAuthorIdent();
                    if(author != null && !author.equals(authorIdent.getName())){
                        continue;
                    }
                    RevWalk rw = new RevWalk(git.getRepository());
                    RevCommit parent = null;
                    if (commit.getParent(0) != null) {
                        parent = rw.parseCommit(commit.getParent(0).getId());
                    }
                    DiffFormatter df = new DiffFormatter(DisabledOutputStream.INSTANCE);
                    df.setRepository(repository);
                    df.setDiffComparator(RawTextComparator.WS_IGNORE_ALL);
                    df.setDetectRenames(true);
                    if (parent == null) {
                        continue;
                    }
                    List<DiffEntry> diffs = df.scan(parent.getTree(), commit.getTree());
                    for (DiffEntry diff : diffs) {
                        paths.add(diff.getNewPath());
                    }
                }else{
                    break;
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            git.close();
        }
        return paths.stream().distinct().collect(Collectors.toList());
    }

    /**
     * 获取git项目文件信息
     */
    public List<ReviewProjectFile> gitFastFiles(String path,ProjectQuery query){
        List<ReviewProjectFile> files = new ArrayList<>();
        Date startDate = query.getStartDate();
        String authorName = query.getAuthor();
        Git git = openRpo(path);
        try {
            List<String> paths = getPaths(path,authorName,startDate);
            System.out.println("查询涉及模块文件数量："+path+" / "+paths.size());
            ThreadServiceUtil.execute((relatePath)->{
                BlameResult result = null;
                try {
                    result = git.blame().setFilePath(relatePath).call();
                } catch (GitAPIException e) {
                    e.printStackTrace();
                }
                if(result == null){
                    return;
                }
                String resultPath = result.getResultPath();
                String fileName = resultPath;
                if(fileName.contains("/")){
                    fileName = resultPath.substring(resultPath.lastIndexOf("/")+1);
                }
                ReviewProjectFile projectFile = new ReviewProjectFile();
                projectFile.setFileId(relatePath);
                projectFile.setFileFlag(Boolean.TRUE);
                projectFile.setFileName(fileName);
                projectFile.setPath(relatePath);

                RawText rawText = result.getResultContents();
                int size = rawText.size();
                List<GitAuthor> authors = new ArrayList<>();
                List<ProjectFileCode> fileCodes = new ArrayList<>();
                for(int i =0 ; i< size ; i++){
                    PersonIdent sourceCommitter = result.getSourceCommitter(i);
                    Date when = sourceCommitter.getWhen();
                    //作者
                    String name = sourceCommitter.getName();
                    String emailAddress = sourceCommitter.getEmailAddress();
                    GitAuthor author = new GitAuthor();
                    author.setUsername(name);
                    author.setEmail(emailAddress);
                    author.setMaxTime(when);

                    GitAuthor auth = authors.stream().filter(a -> a.getUsername().equals(name)).findFirst().orElse(null);
                    if(auth == null){
                        authors.add(author);
                    }else if(auth.getMaxTime().compareTo(when) < 0){
                        auth.setMaxTime(when);
                    }
                    //代码
                    String code = rawText.getString(i);

                    ProjectFileCode fileCode = new ProjectFileCode();
                    fileCode.setFileId(relatePath);
                    fileCode.setCode(code);
                    fileCode.setFileName(fileName);
                    fileCode.setPath(relatePath);
                    fileCode.setNumber(i+1);
                    fileCode.setAuthor(author);
                    fileCode.setLastChangeTime(when);
                    fileCodes.add(fileCode);
                }
                if(!ObjectUtils.isEmpty(authorName)) {
                    GitAuthor author = authors.stream().filter(a -> authorName.equals(a.getUsername())
                            && startDate.compareTo(a.getMaxTime()) <= 0).findFirst().orElse(null);
                    if (author == null) {
                        return;
                    }
                }
                projectFile.setAuthors(authors);
                projectFile.setCodes(fileCodes);
                if(!files.contains(projectFile)) {
                    files.add(projectFile);
                }
            },paths);

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            git.close();
        }
        return files;
    }

    public List<ProjectFileCode> getProjectFileCodes(String path,String relatePath){
        BlameResult result = null;
        Git git = openRpo(path);
        try {
            result = git.blame().setFilePath(relatePath).call();
            if(result == null){
                return null;
            }
            String resultPath = result.getResultPath();
            String fileName = resultPath;
            if(fileName.contains("/")){
                fileName = resultPath.substring(resultPath.lastIndexOf("/")+1);
            }
            RawText rawText = result.getResultContents();
            int size = rawText.size();
            List<GitAuthor> authors = new ArrayList<>();
            List<ProjectFileCode> fileCodes = new ArrayList<>();
            for(int i =0 ; i< size ; i++){
                PersonIdent sourceCommitter = result.getSourceCommitter(i);
                Date when = sourceCommitter.getWhen();
                //作者
                String name = sourceCommitter.getName();
                String emailAddress = sourceCommitter.getEmailAddress();
                GitAuthor author = new GitAuthor();
                author.setUsername(name);
                author.setEmail(emailAddress);
                author.setMaxTime(when);

                GitAuthor auth = authors.stream().filter(a -> a.getUsername().equals(name)).findFirst().orElse(null);
                if(auth == null){
                    authors.add(author);
                }else if(auth.getMaxTime().compareTo(when) < 0){
                    auth.setMaxTime(when);
                }
                //代码
                String code = rawText.getString(i);

                ProjectFileCode fileCode = new ProjectFileCode();
                fileCode.setFileId(relatePath);
                fileCode.setCode(code);
                fileCode.setFileName(fileName);
                fileCode.setPath(relatePath);
                fileCode.setNumber(i+1);
                fileCode.setAuthor(author);
                fileCode.setLastChangeTime(when);
                fileCodes.add(fileCode);
            }
            return fileCodes;
        } catch (GitAPIException e) {
            e.printStackTrace();
        }finally {
            git.close();
        }
        return null;
    }


    private List<ReviewProjectResp> getRespList(Project project){
        List<ReviewProjectResp> projectResps = new ArrayList<>();
        // 获取模块管理器
        ModuleManager moduleManager = ModuleManager.getInstance(project);
        // 获取项目关联的 modules
        Module[] modules = moduleManager.getModules();
        System.out.println("模块数量："+modules.length);
        // 输出模块信息
        for (Module module : modules) {
            ReviewProjectResp resp = new ReviewProjectResp();
            resp.setName(module.getName());
            resp.setPath(IdeaUtils.getModulePath(module));
//            System.out.println(module.getName()+" | "+module.getModuleFilePath());
//            resp.setRespId(UUID.randomUUID().toString());
//            String moduleFilePath = subStringForPoint(module.getModuleFilePath());
//            if(!moduleFilePath.contains(module.getName())){
//                if(moduleFilePath.endsWith("/")){
//                    moduleFilePath = moduleFilePath + module.getName();
//                }else{
//                    moduleFilePath = moduleFilePath + "/" + module.getName();
//                }
//            }
//            System.out.println("模块名称："+moduleFilePath);
//            resp.setPath(moduleFilePath);
            projectResps.add(resp);
        }
        return projectResps;
    }

    private String subStringForPoint(String moduleFilePath){
        if(moduleFilePath.contains(".")) {
            moduleFilePath = moduleFilePath.substring(0, moduleFilePath.lastIndexOf("/"));
            return subStringForPoint(moduleFilePath);
        }else{
            return moduleFilePath;
        }
    }


    public String getUser(String path){
        Repository repository = openRpo(path).getRepository();
        Config config = repository.getConfig();
        String userName = config.getString("user", null, "name");
        String userEmail = config.getString("user", null, "email");
        return ObjectUtils.isEmpty(userEmail) ? userEmail : userName;
    }

    public List<String> getAuthors(String path){
        Set<String> authors = new HashSet<>();
        // 本地仓库路径
        Git git = openRpo(path);
        Iterator<RevCommit> iterator = null;
        try {
            iterator = git.log().call().iterator();
            while(iterator.hasNext()){
                RevCommit cLog = iterator.next();
                PersonIdent authorIdent = cLog.getAuthorIdent();
                authors.add(authorIdent.getName());
            }
        } catch (GitAPIException e) {
            e.printStackTrace();
        }
        return new ArrayList<>(authors);
    }


    /**
     *
     * @param project 项目信息
     * @param operate 1 查询我发起的 2 查询指派给我的
     * @return
     */
    public List<ReviewEntity> getReviewEntity(Project project,int operate){
        List<ReviewEntity> projectResps = new ArrayList<>();
        // 获取模块管理器
        ModuleManager moduleManager = ModuleManager.getInstance(project);
        // 获取项目关联的 modules
        Module[] modules = moduleManager.getModules();
        // 输出模块信息
        for (Module module : modules) {
            ReviewEntity resp = new ReviewEntity();
            resp.setName(module.getName());
            resp.setPath(IdeaUtils.getModulePath(module));
            resp.setProjectList(new ArrayList<>());
            projectResps.add(resp);

            String user = gitService.getUser(resp.getPath());
            //review 信息
            String codeReviewPath = IdeaUtils.codeReviewPath(module);
            List<ReviewContent> reviewContents = IdeaUtils.parseObject(codeReviewPath);
            if(!ObjectUtils.isEmpty(reviewContents)){
                List<CodeReviewProject> collect = reviewContents.stream()
                        .filter(r->{
                            String author = r.getAuthor();
                            String codeAuthor = r.getCodeAuthor();
                            if(operate == 1){
                                return author != null && author.equals(user);
                            }else if(operate == 2){
                                return codeAuthor != null && codeAuthor.equals(user);
                            }else{
                                //不传，查询所有
                                return true;
                            }
                        })
                        .collect(Collectors.groupingBy(ReviewContent::getPath))
                        .entrySet().stream().flatMap(f -> {
                            List<ReviewContent> value = f.getValue();
                            String path = f.getKey();
                            ReviewContent content = value.get(0);

                            CodeReviewProject reviewProject = new CodeReviewProject();
                            reviewProject.setFileName(content.getFileName());
                            reviewProject.setPath(path);
                            reviewProject.setName(content.getFileName());
                            reviewProject.setContentList(value);
                            return Stream.of(reviewProject);
                        }).collect(Collectors.toList());
                resp.setProjectList(collect);
            }
        }
        return projectResps;
    }

}
